package com.boot.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.boot.constants.StatusConstants;
import com.boot.dto.PublishQuestionDTO;
import com.boot.entity.Question;
import com.boot.entity.QuestionAssignment;
import com.boot.entity.Tag;
import com.boot.exception.BusinessException;
import com.boot.mapper.QuestionAssignmentMapper;
import com.boot.mapper.QuestionMapper;
import com.boot.service.IQuestionAssignmentService;
import com.boot.service.IQuestionService;
import com.boot.service.ITagService;
import com.boot.utils.HtmlEscapeUtils;
import com.boot.utils.SecurityUtils;
import com.boot.utils.UserContext;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Service
public class QuestionServiceImpl extends ServiceImpl<QuestionMapper, Question> implements IQuestionService {

    @Resource
    private IQuestionAssignmentService questionAssignmentService;

    @Resource
    private QuestionAssignmentMapper questionAssignmentMapper;

    @Resource
    private ITagService tagService;

    // 定义哪些状态属于“未解决”
    private static final List<String> UNRESOLVED_STATUSES = Collections.unmodifiableList(Arrays.asList(
            StatusConstants.QuestionStatus.PENDING_REVIEW,
            StatusConstants.QuestionStatus.REJECTED,
            StatusConstants.QuestionStatus.WAITING,
            StatusConstants.QuestionStatus.IN_PROGRESS
    ));

    private static final List<String> VALID_HANDLED_STATUSES = Arrays.asList(
            StatusConstants.QuestionStatus.WAITING,
            StatusConstants.QuestionStatus.IN_PROGRESS,
            StatusConstants.QuestionStatus.RESOLVED
    );

    @Override
    public boolean canUserPublish() {
        Long userId = UserContext.getCurrentUserId();
        if (userId == null) return false;

        Long count = this.baseMapper.selectCount(new QueryWrapper<Question>().eq("user_id", userId).in("status", UNRESOLVED_STATUSES));
        return count == 0; // 只有当数量为0时才能发布
    }

    private void checkType(PublishQuestionDTO dto) {
        QueryWrapper<Tag> wrapper = new QueryWrapper<>();
        wrapper.eq("id", dto.getType());
        long count = tagService.count(wrapper);
        if (count == 0) {
            throw new IllegalArgumentException("问题类型不符合");
        }
    }

    @Resource(name = "stringRedisTemplate")
    private StringRedisTemplate redisTemplate;

    private boolean canPublishToday(Long userId, int maxCount) {
        String key = buildKey(userId);
        Long size = redisTemplate.opsForList().size(key);
        return size == null || size < maxCount;
    }

    private String buildKey(Long userId) {
        LocalDate today = LocalDate.now();
        String dateStr = today.format(DateTimeFormatter.BASIC_ISO_DATE); // 如 "20250405"
        return String.format("user:question:count:%d:%s", userId, dateStr);
    }

    private long getSecondsUntilTomorrow() {
        LocalDateTime midnight = LocalDateTime.now().plusDays(1).withHour(0).withMinute(0).withSecond(0).withNano(0);
        return Duration.between(LocalDateTime.now(), midnight).getSeconds();
    }

    private void recordQuestionPublication(Long userId, Long questionId) {
        String key = buildKey(userId);
        redisTemplate.opsForList().rightPush(key, String.valueOf(questionId));

        // 设置过期时间为明天凌晨，自动清空
        long secondsUntilTomorrow = getSecondsUntilTomorrow();
        redisTemplate.expire(key, secondsUntilTomorrow, TimeUnit.SECONDS);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public Long publishQuestion(PublishQuestionDTO dto) {
        checkType(dto);

        Long userId = SecurityUtils.requireCurrentUserId();

        // 在 publishQuestion 开头加入：
        if (!canPublishToday(userId, 3)) {
            throw new BusinessException("今日提问已达上限");
        }

        // 👇 对标题进行 HTML 转义
        String escapedTitle = HtmlEscapeUtils.escapeHtml(dto.getTitle());

        // 1. 创建问题实体
        Question question = new Question();
        question.setTitle(escapedTitle);
        question.setType(dto.getType());
        question.setDescription(dto.getDescription());
        question.setExpected(dto.getExpected());
        question.setUserId(userId);
        question.setStatus(StatusConstants.QuestionStatus.PENDING_REVIEW); // 初始状态：待审核
        question.setCreatedAt(LocalDateTime.now());

        boolean saved = this.save(question);
        if (!saved) {
            throw new BusinessException("问题保存失败");
        }

        // 2. 保存指定专家关系
        List<QuestionAssignment> assignments = dto.getExpertIds().stream()
                .map(expertId -> {
                    QuestionAssignment assignment = new QuestionAssignment();
                    assignment.setQuestionId(question.getId());
                    assignment.setExpertId(expertId);
                    assignment.setAccepted(StatusConstants.AssignmentStatus.PENDING); // 待响应
                    assignment.setRespondedAt(null);
                    return assignment;
                })
                .collect(Collectors.toList());

        boolean assigned = questionAssignmentService.saveBatch(assignments);
        if (!assigned) {
            throw new BusinessException("专家分配信息保存失败");
        }

        recordQuestionPublication(userId, question.getId());

        return question.getId();
    }

    @Override
    public Page<Question> getMyPublishedQuestions(Page<Question> page, Long userId) {
        QueryWrapper<Question> wrapper = new QueryWrapper<Question>()
                .eq("user_id", userId)
                .orderByDesc("created_at");

        return this.page(page, wrapper);
    }

    @Override
    public Page<Question> getHandledQuestionsByExpertId(Page<Question> page, Long expertId) {
        // 1. 查询所有已被该专家接受的任务对应的 question_id
        QueryWrapper<QuestionAssignment> assignmentWrapper = new QueryWrapper<QuestionAssignment>()
                .eq("expert_id", expertId)
                .and(wrapper -> wrapper
                        .isNull("accepted")           // 待响应
                        .or()
                        .eq("accepted", StatusConstants.AssignmentStatus.ACCEPTED)   // 或已接受
                )
                .select("question_id"); // 只取 question_id 字段

        List<Object> assignedQuestionIds = questionAssignmentMapper
                .selectList(assignmentWrapper)
                .stream()
                .map(QuestionAssignment::getQuestionId)
                .collect(Collectors.toList());

        // 2. 如果没有匹配的问题，直接返回空页
        if (assignedQuestionIds.isEmpty()) {
            return new Page<>(page.getCurrent(), page.getSize(), 0);
        }

        // 3. 主表查询这些 question_id 对应的问题，并分页
        QueryWrapper<Question> questionWrapper = new QueryWrapper<Question>()
                .in("id", assignedQuestionIds)
                .in("status", VALID_HANDLED_STATUSES)
                .orderByDesc("created_at");

        return this.page(page, questionWrapper);
    }

    public boolean isQuestionTaken(Long questionId) {
        return questionAssignmentMapper.selectCount(
                new QueryWrapper<QuestionAssignment>()
                        .eq("question_id", questionId)
                        .eq("accepted", StatusConstants.AssignmentStatus.ACCEPTED)
        ) > 0;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void updateQuestion(PublishQuestionDTO dto, Long questionId) {
        Long userId = SecurityUtils.requireCurrentUserId();

        // 1. 查询原问题
        Question existingQuestion = this.getById(questionId);
        if (existingQuestion == null) {
            throw new IllegalArgumentException("问题不存在");
        }

        // 2. 权限校验：必须是提问人，且只能修改 pending_review 状态的问题
        if (!existingQuestion.getUserId().equals(userId)) {
            throw new SecurityException("无权操作他人问题");
        }
        if (!StatusConstants.QuestionStatus.PENDING_REVIEW.equals(existingQuestion.getStatus()) && !StatusConstants.QuestionStatus.REJECTED.equals(existingQuestion.getStatus())) {
            // 若问题已被接单
            if (isQuestionTaken(questionId)) {
                throw new IllegalStateException("问题已被专家接单，无法修改");
            }
        }

        checkType(dto);

        // 👇 对标题进行 HTML 转义
        String escapedTitle = HtmlEscapeUtils.escapeHtml(dto.getTitle());

        // 3. 更新问题信息
        existingQuestion.setTitle(escapedTitle);
        existingQuestion.setType(dto.getType());
        existingQuestion.setDescription(dto.getDescription());
        existingQuestion.setExpected(dto.getExpected());
        existingQuestion.setStatus(StatusConstants.QuestionStatus.PENDING_REVIEW);
        // 注意：不修改 createdAt、userId 等字段

        boolean updated = this.updateById(existingQuestion);
        if (!updated) {
            throw new BusinessException("问题信息更新失败");
        }

        // 4. 删除原有专家分配关系
        QueryWrapper<QuestionAssignment> deleteWrapper = new QueryWrapper<>();
        deleteWrapper.eq("question_id", questionId);
        questionAssignmentService.remove(deleteWrapper);

        // 5. 批量保存新的专家分配关系
        List<QuestionAssignment> assignments = dto.getExpertIds().stream()
                .map(expertId -> {
                    QuestionAssignment assignment = new QuestionAssignment();
                    assignment.setQuestionId(questionId);
                    assignment.setExpertId(expertId);
                    assignment.setAccepted(StatusConstants.AssignmentStatus.PENDING); // 待响应
                    assignment.setRespondedAt(null);
                    return assignment;
                })
                .collect(Collectors.toList());

        if (!assignments.isEmpty()) {
            boolean assigned = questionAssignmentService.saveBatch(assignments);
            if (!assigned) {
                throw new BusinessException("专家分配信息保存失败");
            }
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean markAsResolved(Long questionId) {
        Long currentUserId = SecurityUtils.requireCurrentUserId();

        Question question = this.getById(questionId);
        if (question == null) {
            throw new IllegalArgumentException("问题不存在");
        }

        // 权限校验：必须是提问人
        if (!question.getUserId().equals(currentUserId)) {
            throw new SecurityException("无权操作他人问题");
        }

        // 状态校验：只允许从 in_progress 状态变为 resolved
        List<String> allowedStatuses = Collections.singletonList(
                StatusConstants.QuestionStatus.IN_PROGRESS
        );

        if (!allowedStatuses.contains(question.getStatus())) {
            throw new IllegalStateException("无法将当前状态的问题标记为已解决");
        }

        // 更新状态和解决时间
        question.setStatus(StatusConstants.QuestionStatus.RESOLVED);
        question.setResolvedAt(LocalDateTime.now());

        return this.updateById(question);
    }
}
